﻿#region usings
using System;
using System.Net;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Threading;
using System.Collections.Generic;
using InsuranceAdjuster.OccasionallyConnected.Messages;
using InsuranceAdjuster.Services;
using System.ComponentModel;
#endregion
namespace InsuranceAdjuster.OccasionallyConnected {
    partial class Message {
        static public void ExecuteMessage(Message message,
                                  Action<List<MessageResult>> resultsHandler,
                                  Action messageCompleteHandler) {
            ThreadPool.QueueUserWorkItem(delegate(object x) {
                ThreadedExecuteMessage(message, resultsHandler, messageCompleteHandler);
            });
        }
        static private void ThreadedExecuteMessage(Message message,
                                          Action<List<MessageResult>> resultsHandler,
                                          Action messageCompleteHandler) {

            if (message.IsCompleted) {
                if (messageCompleteHandler != null)
                    messageCompleteHandler.Invoke();
                return;
            }

            if (message.Status == MessageStatus.Created) {
                if (CreateMessage(message, messageCompleteHandler) == false)
                    return;
            }

            if (message.Status == MessageStatus.Preparing) {
                if (PrepareMessage(message, messageCompleteHandler) == false)
                    return;
            }

            if (message.Status == MessageStatus.InQueue) {
                // Get any data from the cache as needed
                if (ProcessMessageCacheLoad(message, resultsHandler, messageCompleteHandler) == false)
                    return;
            }


            if (message.Status == MessageStatus.ProcessingCache
                || message.Status == MessageStatus.InQueue) {

                // Online Only Message?
                if (message.ReturnedCacheResults == true &&
                    message.CacheRules.HasFlag(MessageDataSource.FromServerOnlyIfNoCacheOption)) {

                    // A server action is not needed
                    ProcessMessageCompleted(message, messageCompleteHandler);
                    return;
                }
                else {
                    // Perform server actions required by message
                    ProcessMessageServerLoad(message, resultsHandler, messageCompleteHandler);
                    return;
                }
            }
        }

        private static bool CreateMessage(Message message, Action messageCompleteHandler) {
            message.GenerateIdentifier();
            message.CurrentEntityManager.GetObjectStore().UpdateDurableMessage(message);

            if (message.CurrentEntityManager.CheckForRedundentMessages(message)) {
                message.Status = MessageStatus.Redundent;
                message.Archive();

                if (messageCompleteHandler != null)
                    messageCompleteHandler.Invoke();

                return false;
            }

            message.ExecutionStartedAt = DateTime.Now;
            message.Status = MessageStatus.Preparing;

            return true;
        }
        private static bool PrepareMessage(Message message, Action messageCompleteHandler) {
            var PreProcessResult = message.PreProcessAction();
            if (PreProcessResult == false) {
                message.Status = MessageStatus.Aborted;
                message.ExecutionFinishedAt = DateTime.Now;
                message.Archive();

                if (messageCompleteHandler != null)
                    messageCompleteHandler.Invoke();

                return false;
            }
            message.Status = MessageStatus.InQueue;
            return true;
        }

        static private bool ProcessMessageCacheLoad(Message message, Action<List<MessageResult>> returnHandler, Action messageCompleteHandler) {
            if (message.CacheRules.HasFlag(MessageDataSource.PreferServerOverCache) && EntityManager.Current.IsOnline)
                return true;
            
            // Look for valid Cache returns
            if (message.CacheRules.HasFlag(MessageDataSource.CacheOnly)
                && returnHandler != null) {
                message.Status = MessageStatus.ProcessingCache;

                var CacheResults = message.OfflineOperation(message.CurrentEntityManager.GetObjectStore());
                if (CacheResults.Count > 0)
                    returnHandler.Invoke(CacheResults);

                message.ReturnedCacheResults = CacheResults.Count > 0;
            }

            if (message.CacheRules.HasFlag(MessageDataSource.PreferServerOverCache)) {
                ProcessMessageCompleted(message, messageCompleteHandler);
                return false;
            }
            else
                return true;
        }

        static private void ProcessMessageServerLoad(Message message, Action<List<MessageResult>> returnHandler, Action messageCompleteHandler) {
            message.CurrentEntityManager.GetObjectStore().UpdateDurableMessage(message);
            ThreadPool.QueueUserWorkItem(new WaitCallback(o =>
            {
                while (message.CurrentEntityManager.IsOnline == false) {
                    if (message.Status != MessageStatus.WaitingForNetworkConnection) {
                        message.Status = MessageStatus.WaitingForNetworkConnection;
                        Thread.Sleep(250);
                    }
                }

                // Get a server return
                if (message.CacheRules.HasFlag(MessageDataSource.ServerOnly)) {
                    message.Status = MessageStatus.ProcessingServer;

                    message.OnlineOperation();
                    while (message.IsInFlight)
                        Thread.Sleep(100);

                    message.CurrentEntityManager.GetObjectStore().UpdateDurableMessage(message);

                    if (returnHandler != null) {
                        var ServerResults = message.CompileServerResults();
                        if (ServerResults.Count > 0)
                            returnHandler.Invoke(ServerResults);

                        message.ReturnedServerResults = ServerResults.Count > 0;
                    }
                }

                ProcessMessageCompleted(message, messageCompleteHandler);
            }));
        }

        static private void ProcessMessageCompleted(Message message, Action messageCompleteHandler) {
            message.PostProcessAction();
            message.ExecutionFinishedAt = DateTime.Now;
            message.Status = MessageStatus.Completed;

            if (messageCompleteHandler != null)
                messageCompleteHandler.Invoke();
        }

        static private IInsuranceAdjusterDataService GetDataServiceFromSelector() {
            return new InsuranceAdjuster.Services.InsuranceAdjusterDataService();
        }
    }
}
